#ifndef __MATRIX4D_H__
#define __MATRIX4D_H__

////////////////////////////////////////////////////////////////////////////////
// Includes
////////////////////////////////////////////////////////////////////////////////

#include "Point3D.hpp"
#include "Vector3D.hpp"
#include <cmath>

////////////////////////////////////////////////////////////////////////////////
// Namespaces
////////////////////////////////////////////////////////////////////////////////

namespace Types {

////////////////////////////////////////////////////////////////////////////////
// Implementation
////////////////////////////////////////////////////////////////////////////////

/// Matrix 4D type
template <typename T>
class Matrix4D {

	public:

		/// Ccnstructor
		Matrix4D() {}

		/// Operator []
		T & operator() (const unsigned int i, const unsigned int j) {
			return this->mat[i*4+j];
		}
		
		/// Returns then required item (read-only)
		const T & GetItem(const unsigned int i, const unsigned int j) const {
			return this->mat[i*4+j];
		}

		/// Return the required item
		T & Item(const unsigned int i, const unsigned int j) {
			return this->mat[i*4+j];
		}

		/// Multiply by the given 3D point on the left
		inline const Point3D<T> MultLeft3D(const Point3D<T> & v) const {
			return Point3D<T>(
				v.x*this->mat[0] + v.y*this->mat[4] + v.z*this->mat[8] + this->mat[12],
				v.x*this->mat[1] + v.y*this->mat[5] + v.z*this->mat[9] + this->mat[13],
				v.x*this->mat[2] + v.y*this->mat[6] + v.z*this->mat[10] + this->mat[14]
			);
		}

		/// Multiply by the given 3D vector on the left
		inline const Vector3D<T> TranslateVectorLeft3D(const Vector3D<T> & v) const {
			return Vector3D<T>(
				v.x*this->mat[0] + v.y*this->mat[4] + v.z*this->mat[8],
				v.x*this->mat[1] + v.y*this->mat[5] + v.z*this->mat[9],
				v.x*this->mat[2] + v.y*this->mat[6] + v.z*this->mat[10]
			);
		}

	protected:

		/// Matrix values
		T mat[16];

};

////////////////////////////////////////////////////////////////////////////////
// Closing Namespaces
////////////////////////////////////////////////////////////////////////////////

}

////////////////////////////////////////////////////////////////////////////////

#else

namespace Types {
	template <typename T> class Matrix4D;
}

#endif
